package com.zzedu.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.zzedu.common.annotation.Log;
import com.zzedu.common.core.controller.BaseController;
import com.zzedu.common.core.domain.AjaxResult;
import com.zzedu.common.core.page.TableDataInfo;
import com.zzedu.common.enums.BusinessType;
import com.zzedu.common.exception.ServiceException;
import com.zzedu.common.utils.ServletUtils;
import com.zzedu.common.utils.poi.ExcelUtil;
import com.zzedu.domain.*;
import com.zzedu.dto.OrderSumDTO;
import com.zzedu.dto.ZzStuRenewOrderDTO;
import com.zzedu.framework.lock.IDistributedLockService;
import com.zzedu.framework.transaction.ITransactionHandlerService;
import com.zzedu.params.ZzStuRenewOrderParams;
import com.zzedu.service.IZzSchoolService;
import com.zzedu.service.IZzStuRenewOrderService;
import com.zzedu.service.IZzStudentExtendService;
import com.zzedu.service.IZzStudentService;
import com.zzedu.vo.ZzStuRenewOrderVO;
import io.github.eternalstone.tools.SensitiveFieldUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * (教育-学生)学生续费订单Controller
 *
 * @author Mr.Wang
 * @date 2025-08-16
 */
@RestController
@RequestMapping("/school/studentorder")
public class ZzStuRenewOrderController extends BaseController {
    @Autowired
    private IZzStuRenewOrderService zzStuRenewOrderService;
    @Autowired
    private IZzStudentService zzStudentService;
    @Autowired
    private ITransactionHandlerService transactionHandlerService;

    @Autowired
    private IZzStudentExtendService zzStudentExtendService;

    @Autowired
    private IDistributedLockService distributedLockService;
    @Autowired
    private IZzSchoolService zzSchoolService;

    /**
     * 查询(教育-学生)学生续费订单列表
     */
    @PreAuthorize("@permission.hasAnyPermissions('school:studentorder:query')")
    @RequestMapping("/list")
    public TableDataInfo list(@RequestBody ZzStuRenewOrderDTO zzStuRenewOrder) {
        IPage<ZzStuRenewOrder> page = zzStuRenewOrderService.page(zzStuRenewOrder.getPageDTO(), zzStuRenewOrderService.buildQuery(zzStuRenewOrder));
        return getDataTable(page, zzStuRenewOrderService.convertVO(page.getRecords()));
    }

    /**
     * 导出(教育-学生)学生续费订单列表
     */
    @PreAuthorize("@permission.hasAnyPermissions('school:studentorder:export')")
    @Log(title = "(教育-学生)学生续费订单", businessType = BusinessType.EXPORT)
    @RequestMapping("/export")
    public void export(ZzStuRenewOrderDTO zzStuRenewOrder) {
        List<ZzStuRenewOrder> list = zzStuRenewOrderService.list(zzStuRenewOrderService.buildQuery(zzStuRenewOrder));
        ExcelUtil<ZzStuRenewOrderVO> util = new ExcelUtil<ZzStuRenewOrderVO>(ZzStuRenewOrderVO.class);
        List<ZzStuRenewOrderVO> listVO = zzStuRenewOrderService.convertVO(list);
        for (ZzStuRenewOrderVO stu : listVO) {
            stu.setTel(SensitiveFieldUtil.formatMobile(stu.getTel(), "*"));
        }
        util.exportExcel(ServletUtils.getResponse(), listVO, "订单数据");
    }


    /**
     * 新增保存(教育-学生)学生续费订单
     */
    @PreAuthorize("@permission.hasAnyPermissions('school:studentorder:add')")
    @Log(title = "(教育-学生)学生续费订单", businessType = BusinessType.INSERT)
    @RequestMapping("/add")
    public AjaxResult addSave(@Validated @RequestBody ZzStuRenewOrder zzStuRenewOrder) {
        return toAjax(zzStuRenewOrderService.save(zzStuRenewOrder));
    }


    /**
     * 获取(教育-学生)学生续费订单详细信息
     */
    @PreAuthorize("@permission.hasAnyPermissions('school:studentorder:query')")
    @RequestMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        return success(zzStuRenewOrderService.convertVO(Lists.newArrayList(zzStuRenewOrderService.getById(id))).get(0));
    }


    /**
     * 修改保存(教育-学生)学生续费订单
     */
    @PreAuthorize("@permission.hasAnyPermissions('school:studentorder:edit')")
    @Log(title = "(教育-学生)学生续费订单", businessType = BusinessType.UPDATE)
    @RequestMapping("/edit")
    public AjaxResult editSave(@Validated @RequestBody ZzStuRenewOrderParams params) {
        Long orderId = params.getId();
        String lockName = "update:order:" + orderId;

        // 假设 executeWithLock 是正确方法（原 executeWithOutLock 应为笔误）
        return toAjax(distributedLockService.executeWithLock(lockName, 60L, 60L, TimeUnit.SECONDS, () -> transactionHandlerService.runInTransaction(() -> {

            // ========== 1. 配置映射 ==========
            Map<Integer, Long> daysMap = Map.of(1, 92L, 2, 183L, 3, 275L, 4, 366L);
            Map<Integer, Integer> daysCourse = Map.of(1, 26, 2, 50, 3, 76, 4, 100);

            // 校验 renewType
            if (!daysMap.containsKey(params.getRenewType()) || !daysCourse.containsKey(params.getRenewType())) {
                throw new ServiceException("无效的续费类型: " + params.getRenewType());
            }

            // ========== 2. 获取原订单 ==========
            ZzStuRenewOrder originalOrder = zzStuRenewOrderService.getById(orderId);
            if (originalOrder == null) {
                throw new ServiceException("订单不存在");
            }
            if (originalOrder.getValidFlag() != 1 && originalOrder.getValidFlag() != 2) {
                throw new ServiceException("只有执行中或待执行的订单才能修改");
            }
            Long stuId = originalOrder.getStuId();
            LocalDate newStartDateLocal = params.getStartDate();
            LocalDateTime newStartDate = LocalDateTime.of(newStartDateLocal, LocalTime.MIDNIGHT);
            // 计算偏移天数（用于平移）
            long offsetDays = ChronoUnit.DAYS.between(originalOrder.getStartDate().toLocalDate(), newStartDateLocal);
            // ========== 3. 更新学生课时 ==========
            int oldHours = daysCourse.get(originalOrder.getRenewType());
            int newHours = daysCourse.get(params.getRenewType());
            zzStudentExtendService.update(Wrappers.lambdaUpdate(ZzStudentExtend.class).setDecrBy(ZzStudentExtend::getNumberOfClassHours, oldHours).eq(ZzStudentExtend::getId, stuId));
            zzStudentExtendService.update(Wrappers.lambdaUpdate(ZzStudentExtend.class).setIncrBy(ZzStudentExtend::getNumberOfClassHours, newHours).eq(ZzStudentExtend::getId, stuId));
            // ========== 4. 计算新费用 ==========
            ZzStudent student = zzStudentService.getById(stuId);
            ZzSchool school = zzSchoolService.getById(student.getSchoolId());
            if (school == null) throw new ServiceException("学校不存在");

            BigDecimal referencePrice = school.getReferencePrice();
            Integer feeType = school.getFeeType();
            BigDecimal feeRatio = school.getRatio();

            Assert.notNull(referencePrice, "年参考价不存在");
            Assert.notNull(feeType, "佣金方式不存在");
            Assert.notNull(feeRatio, "佣金比例不存在");

            BigDecimal fee = BigDecimal.ZERO;
            if (1 == feeType) {
                fee = referencePrice.multiply(feeRatio).divide(BigDecimal.valueOf(400), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(params.getRenewType()));
            } else if (2 == feeType) {
                fee = feeRatio.divide(BigDecimal.valueOf(4), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(params.getRenewType()));
            }

            // ========== 5. 构造新有效期 ==========
            LocalDateTime newExpireAt = newStartDate.plusDays(daysMap.get(params.getRenewType()));
            newExpireAt = LocalDateTime.of(newExpireAt.toLocalDate(), LocalTime.of(23, 59, 59));

            // ========== 6. 【关键】校验时间重叠（仅检查 valid_flag IN (1,2) 的订单）==========
            boolean hasOverlap = zzStuRenewOrderService.exists(Wrappers.lambdaQuery(ZzStuRenewOrder.class).eq(ZzStuRenewOrder::getStuId, stuId).ne(ZzStuRenewOrder::getId, orderId).in(ZzStuRenewOrder::getValidFlag, 1, 2) // ← 新增过滤条件
                    .le(ZzStuRenewOrder::getStartDate, newExpireAt)     // existing.start <= new.end
                    .ge(ZzStuRenewOrder::getExpireDate, newStartDate)   // existing.expire >= new.start
            );

            if (hasOverlap) {
                throw new ServiceException("修改后的时间区间与已有有效订单冲突，请调整日期");
            }

            // ========== 7. 更新当前订单 ==========
            originalOrder.setStartDate(newStartDate);
            originalOrder.setExpireDate(newExpireAt);
            originalOrder.setRenewType(params.getRenewType());
            originalOrder.setOrderAmount(fee);
            zzStuRenewOrderService.updateById(originalOrder);

            // ========== 8. 获取所有有效订单（按时间排序，含 valid_flag IN (1,2)）==========
            List<ZzStuRenewOrder> allOrders = zzStuRenewOrderService.list(Wrappers.lambdaQuery(ZzStuRenewOrder.class).eq(ZzStuRenewOrder::getStuId, stuId).in(ZzStuRenewOrder::getValidFlag, 1, 2) // ← 关键：只处理有效订单
                    .orderByAsc(ZzStuRenewOrder::getStartDate));

            // 定位当前订单在时间序列中的位置
            int currentIndex = -1;
            for (int i = 0; i < allOrders.size(); i++) {
                if (allOrders.get(i).getId().equals(orderId)) {
                    currentIndex = i;
                    break;
                }
            }

            if (currentIndex == -1) {
                throw new ServiceException("当前订单不在有效订单列表中");
            }

            // 提取“时间上在当前订单之后”的所有有效订单
            List<ZzStuRenewOrder> futureOrders = Lists.newArrayList();
            if (currentIndex + 1 < allOrders.size()) {
                futureOrders = allOrders.subList(currentIndex + 1, allOrders.size());
            }

            // ========== 9. 平移后续订单 ==========
            LocalDateTime lastExpire = newExpireAt;
            for (ZzStuRenewOrder order : futureOrders) {
                LocalDateTime shiftedStart = order.getStartDate().plusDays(offsetDays);
                LocalDateTime shiftedExpire = order.getExpireDate().plusDays(offsetDays);
                order.setStartDate(shiftedStart);
                order.setExpireDate(shiftedExpire);
                zzStuRenewOrderService.updateById(order);
                lastExpire = shiftedExpire;
            }
            // ========== 10. 更新学生最晚有效期 ==========
            student.setValidTo(lastExpire);
            zzStudentService.updateById(student);

            return true;
        })));
    }

    /**
     * 删除(教育-学生)学生续费订单
     */
    @PreAuthorize("@permission.hasAnyPermissions('school:studentorder:remove')")
    @Log(title = "(教育-学生)学生续费订单", businessType = BusinessType.DELETE)
    @RequestMapping("/remove/{ids}")
    public AjaxResult remove(@PathVariable List<Long> ids) {
        return toAjax(zzStuRenewOrderService.removeBatchByIds(ids));
    }

    @PreAuthorize("@permission.hasAnyPermissions('school:studentorder:abandonment')")
    @Log(title = "(教育-学生)废弃订单", businessType = BusinessType.UPDATE)
    @RequestMapping("/abandonment/{ids}")
    public AjaxResult abandonment(@PathVariable List<Long> ids) {
        return toAjax(zzStuRenewOrderService.abandonment(ids));
    }

    /**
     * 学生续费订单汇总报表
     */
    @PreAuthorize("@permission.hasAnyPermissions('school:studentorder:report')")
    @RequestMapping("/orderSumList")
    public TableDataInfo orderSumList(@RequestBody OrderSumDTO query) {
        return getDataTable(zzStuRenewOrderService.orderSumList(query));
    }

    /**
     * 学生续费订单汇总报表
     */
    @PreAuthorize("@permission.hasAnyPermissions('school:studentorder:report')")
    @RequestMapping("/orderSumListExport")
    public void orderSumListExport(OrderSumDTO query) {
        query.setPageNum(1L);
        query.setPageSize(-1L);
        IPage<OrderSumRenewVO> page = zzStuRenewOrderService.orderSumList(query);
        ExcelUtil<OrderSumRenewVO> util = new ExcelUtil<OrderSumRenewVO>(OrderSumRenewVO.class);
        util.exportExcel(ServletUtils.getResponse(), page.getRecords(), "账单列表");
    }
}
